package cfca.paperless.client.servlet._2_extend._2_03_SignAutoPdfHash;

import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import cfca.com.itextpdf.text.pdf.PdfArray;
import cfca.com.itextpdf.text.pdf.PdfReader;
import cfca.com.itextpdf.text.pdf.security.PdfPKCS7;
import cfca.paperless.client.bean.MultiDataBean;
import cfca.paperless.client.bean.SignLocation;
import cfca.paperless.client.servlet.PaperlessClient;
import cfca.paperless.client.servlet.PaperlessConfig;
import cfca.paperless.client.util.Base64;
import cfca.paperless.client.util.IoUtil;
import cfca.paperless.client.util.PaperlessUtil;
import cfca.paperless.client.util.PdfUtil;
import cfca.paperless.client.util.ReservedPdfPKCS7Util;
import cfca.paperless.client.util.StringUtil;
import cfca.sadk.algorithm.common.Mechanism;
import cfca.sadk.algorithm.sm2.SM2PublicKey;
import cfca.sadk.lib.crypto.bcsoft.BCSoftLib;
import cfca.sadk.util.HashUtil;
import cfca.sadk.x509.certificate.X509Cert;
import cfca.seal.sadk.DonePdfSeal;
import cfca.seal.sadk.PdfSealVerifier;
import cfca.seal.sadk.PrePdfSealExtra;
import cfca.seal.sadk.security.deferred.ReservedPdfPKCS7;

public class SignAutoPdfHashTest02 {

    /**
     * 本地计算hash
     * 然后调用Pdf自动化Hash签名接口
     * 本地合成签名
     * @param args
     * @throws IOException
     * @throws
     */
    public static void main(String[] args) throws Exception {
        try {
            long timeStart = System.currentTimeMillis();// 开始时间
            
            
            String sealCode = "sm2256";//印章编码
            String sealPassword = "cfca1234";
            
            byte[] x509CertData = null;
            
            //以下代码为获取印章编码对应公钥证书
            PaperlessClient assistClientBean = new PaperlessClient(PaperlessConfig.assistUrl, 10000, 60000);// 无纸化辅助接口的访问URL

            String result = assistClientBean.getSealInfo(sealCode, PaperlessConfig.operatorCode);
            String code = StringUtil.getNodeText(result, "Code");
            if (!code.equals("200")) {
                System.out.println("NG:getSealInfo");
                System.out.println(result);
                
                long timeEnd = System.currentTimeMillis();// 结束时间
                System.out.println("time:" + (timeEnd - timeStart));
                
                return;
            } else {
                x509CertData = Base64.decode(StringUtil.getNodeText(result, "SealCert"));
                sealPassword = StringUtil.getNodeText(result, "SealPassword");
            }
            
            
            long timeEnd = System.currentTimeMillis();// 结束时间
            System.out.println("time:" + (timeEnd - timeStart));

            
            
            timeStart = System.currentTimeMillis();// 开始时间
            
            //待签章pdf
            String filePath = "./TestData/autoCalculatePdfHash.pdf";
            byte[] pdf = FileUtils.readFileToByteArray(new File(filePath));
            
            //印模图片
            byte[] handwritingImage = FileUtils.readFileToByteArray(new File("./TestData/zhangfei.png"));
            //如下为本地合成的多媒体信息
            byte[] handwritingData = handwritingImage;
            byte[] identificationFrontData = FileUtils.readFileToByteArray(new File("./TestData/1.png"));
            byte[] identificationBackData = FileUtils.readFileToByteArray(new File("./TestData/2.png"));
            List<MultiDataBean> list = new ArrayList<MultiDataBean>();
            list.add(new MultiDataBean(handwritingData, "handwritingData.dat", "2"));//手写签名数据
            list.add(new MultiDataBean(handwritingImage, "handwritingImage.png", "2"));//手写签名图片
            list.add(new MultiDataBean(identificationFrontData, "identificationFrontImage.png", "2"));//身份证正面
            list.add(new MultiDataBean(identificationBackData, "identificationBackImage.png", "2"));//身份证反面

            //添加多媒体数据到pdf
            pdf = addMultiDataToPdf(pdf, list);
            
            X509Cert x509Cert = new X509Cert(x509CertData);
            
            String hashAlogrithmArg = "SM3";
            
            SignLocation signLocation = new SignLocation("代理人签名", "R", 10, 0);//签名位置
            
            ReservedPdfPKCS7 reservedPdfPKCS7 = new ReservedPdfPKCS7();//创建临时数据保存对象
            //计算保留值
            String timestampServer[] = new String[]{"http://210.74.41.195/timestamp","",""};
            
            PrePdfSealExtra prePdfSealExtra = new PrePdfSealExtra("签合同", "北京");
            
            byte[] reservedPdf = ReservedPdfPKCS7Util.createReservedPdf(pdf, handwritingImage, 0.75f, x509Cert, hashAlogrithmArg, signLocation, reservedPdfPKCS7, timestampServer, prePdfSealExtra);
            //如下数据需要保存到数据库
            String reservedData = ReservedPdfPKCS7Util.createReservedVar(reservedPdfPKCS7);
            //计算hash
            byte[] pdfSealHash = HashUtil.SM3HashDataWithZValue(reservedPdfPKCS7.signatureAttr,(SM2PublicKey)x509Cert.getPublicKey(), BCSoftLib.INSTANCE());
            
            
            timeEnd = System.currentTimeMillis();// 结束时间
            System.out.println("time:" + (timeEnd - timeStart));

            
            
            timeStart = System.currentTimeMillis();// 开始时间
            
            //初始化客户端对象
            PaperlessClient extClientBean = new PaperlessClient(PaperlessConfig.extUrl, 10000, 60000);// 无纸化辅助接口的访问URL

            //组装PdfHash签名接口报文
            String pdfHashSignListXml = "<List><PdfHashSign>" +
                                        "<PdfSealHash>" + Base64.toBase64String(pdfSealHash) + "</PdfSealHash>" +
                                        "<PdfSealSource></PdfSealSource>" +
                                        "<BizTypeCode></BizTypeCode>" +
                                        "<SealCode>" + sealCode + "</SealCode>" +
                                        "<SealPassword>" + sealPassword + "</SealPassword>" +
                                        "</PdfHashSign></List>";
            
            //组装短信验证码
            //String authCodeXml = "<AuthCode><AuthCodeValue>063648</AuthCodeValue><AuthCodeId>201608171509163866694567773698</AuthCodeId></AuthCode>";
            
            //调用 pdfhash签名接口
            String signMessage = extClientBean.signAutoPdfHash(pdfHashSignListXml, "", PaperlessConfig.operatorCode, PaperlessConfig.channelCode);
            
            code = StringUtil.getNodeText(signMessage, "Code");
            if (!"200".equals(code)) {
                System.out.println("NG:signAutoPdfHash");
                System.out.println("" + signMessage);
                
                timeEnd = System.currentTimeMillis();// 结束时间
                System.out.println("time:" + (timeEnd - timeStart));
                
                return;
            }
        
            System.out.println("OK:autoCalculatePdfHash");
            Object[] signedBeanArr = StringUtil.getXmlFieldArr(signMessage, "SignedBean");
            
            //获取签名值
            String pkcs1 = StringUtil.getNodeText((String)signedBeanArr[0], "Pkcs1");
            System.out.println("返回的pkcs1：" + pkcs1);
            
            //恢复reservedPdfPKCS7对象
            reservedPdfPKCS7 = ReservedPdfPKCS7Util.initReservedVar(reservedData);
            reservedPdfPKCS7.signaturePKCS1 = Base64.decode(pkcs1);
            
            //合成外部签名
            DonePdfSeal donePdfSeal = new DonePdfSeal();
            byte[] sealedPdfData = donePdfSeal.mergeReservedPdfSeal(new PdfReader(reservedPdf), reservedPdfPKCS7);
            
            
            timeEnd = System.currentTimeMillis();// 结束时间
            System.out.println("time:" + (timeEnd - timeStart));

            
            
            timeStart = System.currentTimeMillis();// 开始时间
            
            //验证 pdf签章
            PdfSealVerifier pdfSealVerifier = new PdfSealVerifier(sealedPdfData);
            if (!pdfSealVerifier.isPKCS7Valid()) {
                System.out.println("pdfSealVerifier error");
            } else {
                PdfArray uncoverdRange = pdfSealVerifier.getUncoveredByteRange();
                long uncoverdRangeLen = uncoverdRange.asLongArray()[1];
                if (uncoverdRangeLen > 0) {
                    System.out.println(" 单据在盖章之后被修改 ");
                }
                //验证后获取签章时间和证书主题
                Map<String, X509Cert> sigNameCertMap = pdfSealVerifier.getSigNameCertMap();
                Map<String, PdfPKCS7> sigNamePKCS7Map = pdfSealVerifier.getSigNamePKCS7Map();
                List<X509Cert> x509CertList = new ArrayList<X509Cert>(sigNameCertMap.values());
                List<PdfPKCS7> pdfPKCS7List = new ArrayList<PdfPKCS7>(sigNamePKCS7Map.values());
                Calendar calendar = pdfPKCS7List.get(0).getSignDate();
                Date date = calendar.getTime();
                
                System.out.println("签章时间：" + date.toLocaleString());
                System.out.println("签章证书主题:" + x509CertList.get(0).getSubject());
                System.out.println("签章证书序列号:" + x509CertList.get(0).getStringSerialNumber());
                
                String outputFilePath = "./TestData/output/signAutoPdfProofHash2-" + new File(filePath).getName() + ".pdf";
                System.out.println("pdfSealVerifier ok " + outputFilePath);
                IoUtil.write(outputFilePath, sealedPdfData);
            }

            timeEnd = System.currentTimeMillis();// 结束时间
            System.out.println("time:" + (timeEnd - timeStart));

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    
    private static byte[] addMultiDataToPdf(byte[] pdf, List<MultiDataBean> list) throws Exception {
        String proofHashAndMultiData1[] = PaperlessUtil.getProofHashAndMultiData(list);
        String proofHashXml = proofHashAndMultiData1[0];
        //计算pdf内容hash
        String pdfContentHash = PdfUtil.calculatePdfContentHash(pdf, new Mechanism(Mechanism.SHA1));
        proofHashXml = addProofToXml(proofHashXml, "pdfHash.pdf", pdfContentHash);
        //合成proofHash
        pdf = PdfUtil.compoundPdfDataByAttachment(pdf, proofHashXml.getBytes(), "proofHash.xml");
        //合成多媒体文件
        for(MultiDataBean multiDataBean : list) {
            pdf = PdfUtil.compoundPdfDataByAttachment(pdf, multiDataBean.getMultiData(), multiDataBean.getFileName());
        }
        return pdf;
    }
    
    
    public static String addProofToXml(String xmlContent, String fileName, String hash) throws DocumentException  {
        Document document = DocumentHelper.parseText(xmlContent);
        Element root = document.getRootElement();
        Element newElement = root.addElement("Proof");
        newElement.addAttribute("fileName", fileName);
        newElement.addAttribute("hash", hash);
        return document.asXML();
    }
}
